#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <dirent.h>

#include "ak_common.h"
#include "ak_vi.h"

/* default sensor config file path */
#define FIRST_PATH			"/etc/jffs2/"
#define SECOND_PATH			"/usr/local/"

/* switch for debugging */
#define SAVE_YUV_DATT		1	//save yuv frame data
#define SWITCH_RESOLUTION	0	//switch sensor resolution
#define CALC_CAP_TIME		0	//for calculate use time

struct vi_demo {
	int run_flag;
	int cap_num;
	char *save_path;
	void *vi_handle;
};

static struct vi_demo vi_ctrl = {0};

static void *init_video_in(void)
{
	/* must match sensor before vi open */
	if (ak_vi_match_sensor(FIRST_PATH)) {
		if (ak_vi_match_sensor(SECOND_PATH)) {
			ak_print_error_ex("match sensor failed, "
					"check your config file or sensor driver\n");
			return NULL;
		}
	}

	/* vi open mast after match success*/
	void *handle = NULL;
	handle = ak_vi_open(VIDEO_DEV0);
	if (NULL == handle) {
    	ak_print_normal("########## ak_vi_open failed!\n");
    	return NULL;
	}

 	/* get sensor resolution */
	struct video_resolution sensor_res = {0};
	if (ak_vi_get_sensor_resolution(handle, &sensor_res)) {
		ak_print_normal("ak_mpi_vi_get_sensor_res failed!\n");
	}

	/* set crop information */
	struct video_channel_attr *attr  = NULL;
	attr = (struct video_channel_attr *)calloc(1, sizeof(struct video_channel_attr));

	attr->res[VIDEO_CHN_MAIN].width = sensor_res.width;
	attr->res[VIDEO_CHN_MAIN].height = sensor_res.height;

	attr->res[VIDEO_CHN_SUB].width = 640;
	attr->res[VIDEO_CHN_SUB].height = 480;

	attr->crop.left = 0;
	attr->crop.top = 0;
	attr->crop.width = sensor_res.width;
	attr->crop.height = sensor_res.height;

	if (ak_vi_set_channel_attr(handle, attr)) {
		ak_print_normal("ak_vi_set_channel_attr failed!\n");
	}

	/* get crop */
	struct video_channel_attr cur_attr;

	memset(&cur_attr, 0x00, sizeof(cur_attr));
	if (ak_vi_get_channel_attr(handle, &cur_attr)) {
		ak_print_normal("ak_vi_get_channel_attr failed!\n");
	}

	ak_print_normal("capture fps: %d\n", ak_vi_get_fps(handle));

    return handle;
}

static void exit_vi_demo(void)
{
	/* close video in model */
	ak_vi_capture_off(vi_ctrl.vi_handle);
	ak_vi_close(vi_ctrl.vi_handle);
	ak_print_normal("**********video input demo finish************\n\n");
}

static void process_signal(int sig)
{
	ak_print_notice("\t signal %d caught", sig);
	if((SIGTERM == sig) || (SIGINT == sig) || (SIGSEGV == sig)){
		vi_ctrl.run_flag = AK_FALSE;
	}
}

/* register signal that we should handle */
static int register_signal(void)
{
	signal(SIGSEGV, process_signal);
	signal(SIGINT, process_signal);
	signal(SIGTERM, process_signal);
	signal(SIGCHLD, SIG_IGN);

	return 0;
}

#if SAVE_YUV_DATT
static void save_yuv_data(int index, struct video_input_frame *frame)
{
	FILE *fd = NULL;
	unsigned int len = 0;
	unsigned char *buf = NULL;
	struct ak_date date;
	char time_str[32] = {0};
	char file_path[255] = {0};

	/* save the YUV frame data */
	ak_print_normal_ex("saving frame, index=%d\n", index);

	ak_get_localdate(&date);
	ak_date_to_string(&date, time_str);
	sprintf(file_path, "%smain_%s_%d.yuv", vi_ctrl.save_path, time_str, index);

	/* open appointed file to save YUV data */
	fd = fopen(file_path, "w+b");
	if (fd) {
		buf = frame->vi_frame[VIDEO_CHN_MAIN].data;
		len = frame->vi_frame[VIDEO_CHN_MAIN].len;
		ak_print_normal("yuv len: %u\n", len);
		do {
			len -= fwrite(buf, 1, len, fd);
		} while (len != 0);
	} else {
		ak_print_normal("open YUV file failed!!\n");
	}
	if (fd)
		fclose(fd);

	/* generate YUV file name */
	sprintf(file_path, "%ssub_%s_%d.yuv", vi_ctrl.save_path, time_str, index);

	fd = fopen(file_path, "w+b");
	if (fd) {
		buf = frame->vi_frame[VIDEO_CHN_SUB].data;
		len = frame->vi_frame[VIDEO_CHN_SUB].len;
		ak_print_normal("yuv len: %u\n", len);
		do {
			len -= fwrite(buf, 1, len, fd);
		} while (len != 0);
	} else {
		ak_print_normal("open YUV file failed!!\n");
	}
	if (fd)
		fclose(fd);
}
#endif

#if SWITCH_RESOLUTION
static void switch_sensor_resolution(void)
{
	struct video_channel_attr attr;

	attr.res[VIDEO_CHN_MAIN].width = 720;
	attr.res[VIDEO_CHN_MAIN].height = 480;

	attr.res[VIDEO_CHN_SUB].width = 360;
	attr.res[VIDEO_CHN_SUB].height = 240;

	attr.crop.left = 0;
	attr.crop.top = 0;
	attr.crop.width = 1280;
	attr.crop.height = 720;

	ak_vi_change_channel_attr(vi_ctrl.vi_handle, &attr);
}
#endif

static void vi_work(void)
{
	struct video_input_frame frame = {{{0}, {0}}};
	int index = 0;
	unsigned long pre_seq_no = 0;
#if CALC_CAP_TIME
	struct ak_timeval tv_start
	struct ak_timeval tv_end;
	ak_get_ostime(&tv_start);
#endif

	/* the first 5 frame have to drop,get the sixth frame */
    while (vi_ctrl.run_flag && (index < vi_ctrl.cap_num)) {
#if SWITCH_RESOLUTION
		if (4 == index) {
			/* auto set the resolution */
			switch_sensor_resolution();
		}
#endif
		memset(&frame, 0x00, sizeof(frame));
		int ret = ak_vi_get_frame(vi_ctrl.vi_handle, &frame);
#if CALC_CAP_TIME
		ak_get_ostime(&tv_end);
		ak_print_normal("get frame use: %ld(ms)\n",
			ak_diff_ms_time(&tv_end, &tv_start));
		ak_get_ostime(&tv_start);
#endif
		if (!ret) {
#if SAVE_YUV_DATT
			save_yuv_data(index, &frame);
#endif
			if (frame.vi_frame[VIDEO_CHN_MAIN].seq_no != (pre_seq_no + 1)) {
				ak_print_normal_ex("video: ts=%llu, len=%u, seq_no=%lu\n",
	    			frame.vi_frame[VIDEO_CHN_MAIN].ts,
	    			frame.vi_frame[VIDEO_CHN_MAIN].len,
	    			frame.vi_frame[VIDEO_CHN_MAIN].seq_no);
			}
			pre_seq_no = frame.vi_frame[VIDEO_CHN_MAIN].seq_no;

			/* the frame has used,release the frame data */
			ak_vi_release_frame(vi_ctrl.vi_handle, &frame);
			index++;
		}
	}

	ak_print_normal_ex("over\n");
}

int main(int argc, char **argv)
{
	ak_print_normal("**********video input demo begin************\n\n");

	if (argc != 3) {
		ak_print_normal("%s capture frames and store to 'save_path'\n", argv[0]);
		ak_print_normal("Usage: %s n save_path\n", argv[0]);
		ak_print_normal("e.g: %s 10 /mnt/\n", argv[0]);
		return -1;
	}

	int capture_num = atoi(argv[1]);
	if (capture_num <= 0) {
		ak_print_normal("invalid arguments, 'n' must big than zero\n");
		return -1;
	} else
		ak_print_normal("capture %d frame\n", capture_num);

	register_signal();

	/* thread1 open */
	vi_ctrl.cap_num = capture_num;
	vi_ctrl.save_path = argv[2];
    vi_ctrl.vi_handle = init_video_in();
	if (!vi_ctrl.vi_handle) {
		return -1;
	}
	ak_print_normal("open vi ok\n");

	vi_ctrl.run_flag = AK_TRUE;
	ak_vi_capture_on(vi_ctrl.vi_handle);

	vi_work();

	ak_print_normal("main thread wait work thread exit \n");

	exit_vi_demo();

	return 0;
}
